package com.caoustc.okhttplib.okhttp

import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import com.alibaba.fastjson.JSON
import com.alibaba.fastjson.JSONArray
import com.alibaba.fastjson.JSONObject
import com.caoustc.okhttplib.okhttp.ProgressRequestBody.OkHttpProgressCallback
import com.caoustc.okhttplib.okhttp.model.BaseDataObject
import com.caoustc.okhttplib.okhttp.model.BaseDataObject2
import com.caoustc.okhttplib.okhttp.platform.Stat
import com.caoustc.okhttplib.okhttp.utils.OkHttpUtils
import com.caoustc.okhttplib.okhttp.utils.ParseUtils
import com.caoustc.okhttplib.utils.StringUtils
import com.socks.library.KLog
import okhttp3.*
import java.io.IOException
import java.io.InterruptedIOException
import java.net.ConnectException
import java.net.SocketException
import java.net.SocketTimeoutException

class OkHttpTask(
    private val isCancel: Boolean,
    private val method: OkHttpMethod,
    private var url: String?,
    params: OkHttpRequestParams?,
    builder: OkHttpClient.Builder?,
    private val callback: BaseHttpRequestCallback<Any?>?
) : Callback, OkHttpProgressCallback {
    private val handler = Handler(Looper.getMainLooper())
    private var params: OkHttpRequestParams? = null
    private var headers: Headers? = null
    private var requestKey: String?
    private val okHttpClient: OkHttpClient?
    private var currentTag = 0
    private var srcUrl: String? = null

    /**
     * 默认解析参数
     */
    private var parseKey: String? = "data"
    private var parseKeyTwo = "data"
    private var dataClass: Class<*>? = null
    private var showOnFailure = false
    fun setCurrentTag(currentTag: Int) {
        this.currentTag = currentTag
    }

    fun setParseKey(parseKey: String?) {
        if (!TextUtils.isEmpty(parseKey)) {
            this.parseKey = parseKey
        }
    }

    fun setParseKeyTwo(parseKeyTwo: String) {
        if (!TextUtils.isEmpty(parseKeyTwo)) {
            this.parseKeyTwo = parseKeyTwo
        }
    }

    fun setDataClass(dataClass: Class<*>?, showOnFailure: Boolean) {
        this.dataClass = dataClass
        this.showOnFailure = showOnFailure
    }

    protected fun execute() {
        execute(false, null)
    }

    protected fun execute(tagUrl: String?) {
        execute(false, tagUrl)
    }

    fun execute(showOnStart: Boolean, tagUrl: String?) {
        if (params?.headers != null) {
            headers = params?.headers?.build()
        }
        if (showOnStart) {
            OkHttpManager.instance?.httpErrorManager?.showOnStart(url)
        }
        callback?.onStart()
        try {
            run(tagUrl)
        } catch (e: Exception) {
            KLog.e("okhttp", e.message)
        }
    }

    @Throws(Exception::class)
    protected fun run(tagUrl: String?) {
        srcUrl = tagUrl ?: url
        val builder = Request.Builder()
        when (method) {
            OkHttpMethod.GET -> {
                url = OkHttpUtils.getFullUrl(url, params?.formParams, params?.isUrlEncoder == true)
                builder.get()
            }
            OkHttpMethod.DELETE -> {
                url = OkHttpUtils.getFullUrl(url, params?.formParams, params?.isUrlEncoder == true)
                builder.delete()
            }
            OkHttpMethod.HEAD -> {
                url = OkHttpUtils.getFullUrl(url, params?.formParams, params?.isUrlEncoder == true)
                builder.head()
            }
            OkHttpMethod.POST -> {
                val body = params?.requestBody
                if (body != null) {
                    builder.post(ProgressRequestBody(body, this))
                }
            }
            OkHttpMethod.PUT -> {
                val bodyPut = params?.requestBody
                if (bodyPut != null) {
                    builder.put(ProgressRequestBody(bodyPut, this))
                }
            }
            OkHttpMethod.PATCH -> {
                val bodyPatch = params!!.requestBody
                if (bodyPatch != null) {
                    builder.patch(ProgressRequestBody(bodyPatch, this))
                }
            }
            else -> {
            }
        }
        params?.cacheControl?.let {
            builder.cacheControl(it)
        }

        builder.url(url).tag(srcUrl).headers(headers)
        val request = builder.build()
        if (OkHttpConstants.DEBUG) {
            KLog.d(
                "okhttp",
                "url=" + (if (tagUrl == null) srcUrl else url) + "?" + params.toString()
            )
        }
//        if (isCancel) {
////            OkHttpRequest.cancelUrl(srcUrl);
//        }
        val call = okHttpClient?.newCall(request)
        OkHttpCallManager.instance?.addCall(srcUrl, call)
        OkHttpCallManager.instance?.addCallTag(srcUrl, currentTag)
        call?.enqueue(this)
    }

    override fun updateProgress(progress: Int, networkSpeed: Long, done: Boolean) {
        handler.post { callback?.onProgress(progress, networkSpeed, done) }
    }

    override fun onFailure(call: Call, e: IOException) {
        KLog.i(TAG, "onFailure()")
        val responseData = OkHttpResponseData()
        if (e is SocketTimeoutException) {
            responseData.isTimeout = true
            responseData.isSocketException = false
        } else if (e is InterruptedIOException && TextUtils.equals(e.message, "timeout")) {
            responseData.isTimeout = true
            responseData.isSocketException = false
        } else if (e is ConnectException) {
            responseData.isSocketException = false
            responseData.isTimeout = false
        } else if (e is SocketException && e.toString().contains("closed")) {
            responseData.isSocketException = true
            responseData.isTimeout = false
        }
        handlerResponse(responseData, null)
    }

    @Throws(IOException::class)
    override fun onResponse(call: Call, response: Response) {
        val responseData = OkHttpResponseData()
        handlerResponse(responseData, response)
    }

    /**
     * 处理数据回调.
     * @param responseData
     * @param response
     */
    private fun handlerResponse(responseData: OkHttpResponseData, response: Response?) {
        try {
            if (isCancel
                && OkHttpCallManager.instance?.getCallTag(srcUrl) != null
                && currentTag < OkHttpCallManager.instance?.getCallTag(srcUrl)!!
            ) {
                KLog.d("okhttp", "request canceled")
                return
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        if (response != null) {
            responseData.isResponseNull = false
            responseData.code = response.code()
            responseData.message = response.message()
            responseData.isSuccess = response.isSuccessful
            try {
                val respBody = response.body()!!.string()
                responseData.response = respBody
                responseData.headers = response.headers()
                KLog.i(TAG, "handlerResponse()#response>>>" + responseData.response)
            } catch (e: IOException) {
                KLog.e("okhttp", e.message)
                responseData.isResponseNull = true
                responseData.isTimeout = false
                responseData.code = Constants.ERROR_CANCEL_REQUEST
                responseData.message = "连接失败，请检查您的网络设置"
            }
        } else {
            responseData.isResponseNull = true
            if (responseData.isSocketException) {
                responseData.isTimeout = false
                responseData.code = Constants.ERROR_CANCEL_REQUEST
                responseData.message = "连接失败，请检查您的网络设置"
            } else {
                if (responseData.isTimeout) {
                    responseData.code = Constants.ERROR_RESPONSE_TIMEOUT
                    responseData.message = "连接超时，请检查您的网络设置"
                } else {
                    responseData.code = Constants.ERROR_RESPONSE_UNKNOWN
                    responseData.message = "连接失败，请检查您的网络设置"
                }
            }
        }
        responseData.httpResponse = response
        handler.post { onPostExecute(responseData) }
    }

    /**
     * 数据处理.
     * @param responseData
     */
    protected fun onPostExecute(responseData: OkHttpResponseData) {
        OkHttpCallManager.instance?.removeCall(srcUrl)
        if (HttpTaskHandler.instance?.contains(requestKey) == false) {
            return
        }

        val code = responseData.code
        val msg = responseData.message
        if (!responseData.isResponseNull) {
            if (responseData.isSuccess) {
                val respBody = responseData.response
                if (OkHttpConstants.DEBUG) {
                    KLog.d("okhttp", "url=" + url + "?" + params.toString())
                    try {
                        KLog.json("okhttp", respBody)
                    } catch (e: Exception) {
                        KLog.d("okhttp", "s json parse error b")
                    }
                }
                parseResponseBody(responseData, callback)
            } else { //请求失败
                if (OkHttpConstants.DEBUG) {
                    KLog.d("okhttp", "url=$url\n response failure code=$code msg=$msg")
                }
                if (callback != null) {
                    parseFailure(code, responseData.message)
                }
            }
        } else {
            if (OkHttpConstants.DEBUG) {
                KLog.d("okhttp", "url=$url\n response failure code=$code msg=$msg")
            }
            if (callback != null) {
                if (OkHttpManager.instance?.isReturnSocketException == false) {
                    if (!responseData.isSocketException) {
                        parseFailure(code, responseData.message)
                    }
                } else {
                    parseFailure(code, responseData.message)
                }
            }
        }
        callback?.onFinish()
    }

    /**
     * 数据解析.
     * @param responseData
     * @param callback
     */
    private fun parseResponseBody(
        responseData: OkHttpResponseData,
        callback: BaseHttpRequestCallback<Any?>?
    ) {
        //回调为空，不向下执行
        if (callback == null) {
            return
        }
        val result = responseData.response
        if (StringUtils.Companion.isEmpty(result)) {
            parseFailure(Constants.ERROR_RESPONSE_NULL, "服务异常")
        }
        if (callback.baseRequestType === String::class.java) { //字符串类型.
            //callback.onSuccess(responseData.getHeaders(), result);
            callback.onSuccess(result)
            return
        } else if (callback.baseRequestType === JSONObject::class.java) { //JsonObject .
            var jsonObject: JSONObject? = null
            try {
                jsonObject = JSON.parseObject(result)
            } catch (e: Exception) {
                KLog.e("okhttp", e.message)
            }
            if (jsonObject != null) {
                callback.onSuccess(jsonObject)
                return
            }
        } else if (callback.baseRequestType === JSONArray::class.java) { //JsonArray .
            var jsonArray: JSONArray? = null
            try {
                jsonArray = JSON.parseArray(result)
            } catch (e: Exception) {
                KLog.e("okhttp", e.message)
            }
            if (jsonArray != null) {
                callback.onSuccess(jsonArray)
                return
            }
        } else if (callback.baseDataType === BaseDataObject::class.java || callback.baseDataType === BaseDataObject2::class.java) { //空对象 .
            var jsonObject: JSONObject? = null
            try {
                jsonObject = JSON.parseObject(result)
                if (jsonObject != null) {
                    //get "result" data .
                    val resultCode = ParseUtils.parseResult(jsonObject, "result")
                    val desc = ParseUtils.parseResult(jsonObject, "desc")
                    val isError = ParseUtils.parseBoolean(jsonObject, "isError")
                    if (!StringUtils.Companion.isResultOk(resultCode, isError, desc)) { //如果结果不正确 .
                        if (OkHttpManager.instance?.httpErrorManager != null) {
                            callback.onSuccessError(
                                resultCode,
                                OkHttpManager.instance?.httpErrorManager?.onResultMessage(resultCode)
                            )
                        } else {
                            callback.onSuccessError(resultCode, "服务异常")
                        }
                        return
                    }
                    if (callback.baseDataType === BaseDataObject2::class.java) {
                        if (jsonObject.containsKey(parseKey)) { //去掉一层data.
                            val dataObject = jsonObject.getJSONObject(parseKey)
                            val total = ParseUtils.parseTotal(dataObject, "total")
                            val obj = ParseUtils.parseJson(
                                dataObject,
                                parseKeyTwo,
                                callback.baseRequestType,
                                dataClass
                            )
                            callback.onSuccess(obj)
                            callback.onSuccess(obj, total)
                            return
                        }
                    } else if (callback.baseDataType === BaseDataObject::class.java) { //1-2层data数据结构解析.
                        val obj = ParseUtils.parseJson(
                            jsonObject,
                            parseKey,
                            callback.baseRequestType,
                            dataClass
                        )
                        callback.onSuccess(obj)
                        return
                    }
                }
            } catch (e: Exception) {
                KLog.e("okhttp", e.message)
            }
        } else if (callback.baseDataType === Stat::class.java) {
            var jsonObject: JSONObject? = null
            try {
                jsonObject = JSON.parseObject(result)
                if (jsonObject != null) {
                    val mStat = ParseUtils.parseStat(jsonObject)
                    if (!StringUtils.Companion.isResultSuccess(mStat)) {
                        callback.onSuccessError(mStat)
                        return
                    }
                    val total = ParseUtils.parseTotal(jsonObject, "total")
                    val obj = ParseUtils.parseJson(
                        jsonObject,
                        parseKey,
                        callback.baseRequestType,
                        dataClass
                    )
                    callback.onSuccess(obj, mStat)
                    callback.onSuccess(obj, mStat, total)
                    return
                }
            } catch (e: Exception) {
                KLog.e("okhttp", e.message)
            }
        } else {
            var obj: Any? = null
            try {
                obj = JSON.parseObject(result, callback.baseRequestType)
                if (obj != null) {
                    callback.onSuccess(obj)
                    return
                }
            } catch (e: Exception) {
                KLog.e("okhttp", e.message)
            }
        }
        parseFailure(Constants.ERROR_RESPONSE_DATA_PARSE_EXCEPTION, "服务异常")
    }

    private fun parseFailure(errorCode: Int, msg: String?) {
        val mHttpErrorManager: OkHttpErrorManager? = OkHttpManager.instance?.httpErrorManager
        if (OkHttpManager.instance != null && mHttpErrorManager != null) {
            val errorMsg = mHttpErrorManager.getErrorMessage(errorCode)
            if (showOnFailure) {
                mHttpErrorManager.showOnFailure(errorCode, errorMsg)
            }
            callback?.onFailure(errorCode, errorMsg)
            return
        }
        callback?.onFailure(errorCode, msg)
    }

    companion object {
        private const val TAG = "OkHttpTask"
        const val DEFAULT_HTTP_TASK_KEY = "default_http_task_key"
    }

    init {
        if (params == null) {
            this.params = OkHttpRequestParams()
        } else {
            this.params = params
        }
        requestKey = this.params?.httpTaskKey
        if (StringUtils.isEmpty(requestKey)) {
            requestKey = DEFAULT_HTTP_TASK_KEY
        }
        HttpTaskHandler.instance?.addTask(requestKey, url)
        okHttpClient = builder?.build()
    }
}